﻿#region Creditos

/* Autor: Edilberto Sánchez Forero	*/
/* Email: esanchezfo@gmail.com		*/
/* Licencia: LGPL			*/
/* País: Colombia, Bogotá			*/

#endregion

#region Librerias

using System;
using System.Collections.Generic;
using ESF.General;
using ESF.SSO.Core.Entities;
using ESF.SSO.Core.Entities.Exceptions;
using ESF.SSO.Core.Entities.Log;
using ESF.SSO.Core.DataAccess;
using ESF.General.Entity;
using ESF.SSO.Core.Entities.BasicReferences;
using ESF.SSO.Core.DataAccess.BasicReferences;
using System.Net.Mail;
using System.Text;
using ESF.Data;
using System.IO;
using System.Linq;

#endregion


namespace ESF.SSO.Core.BusinessLogic
{
	public static class CtrlUser
	{
		#region Custom Lists

		public static List<string> ListUserEnterprises()
		{
			return AdoUser.ListUserEnterprises ();
		}

		#endregion


		#region Values

		public static List<BasicReference> GetUserBasicReferenceValues(User pUser, BasicReferenceTypes pBasicReferenceType)
		{
			return AdoUser.GetUserBasicReferenceValues (pUser, pBasicReferenceType);
		}


		public static BasicReference GetUserBasicReferenceValue (User pUser, int pBasicReferenceTypeID, string pCode)
		{
			return GetUserBasicReferenceValue (pUser, new BasicReference () { Code = pCode, BasicReferenceType = new BasicReferenceType () { ID = pBasicReferenceTypeID } });
		}


		public static BasicReference GetUserBasicReferenceValue (User pUser, BasicReference pRef)
		{
			BasicReference value;
			
			
			#region Validacion

			Validate.FilledBasRefPart ("Referencia", pRef);
			Validate.FilledObject (true, "Tipo de la Referencia", pRef.BasicReferenceType);
			Validate.FilledID ("Identificador del Tipo de la Referencia", pRef.BasicReferenceType.ID);
			Validate.FilledObject (true, "Usuario", pUser);
			Validate.FilledID ("Alias de Usuario Solicitante", pUser.Alias);

			#endregion

			value = AdoUser.GetUserBasicReferenceValues (pUser, pRef);

			if (value == null)
			{
				throw new GenericException (string.Format ("The value for basic reference {0} with type {1} and user {2} ({3}) was not found", pRef.Code, pRef.BasicReferenceType.ID, pUser.Alias, pUser.ID));
			}

			return value;
		}

		
		public static void DeleteUserBasicReferences (User pUser, BasicReferenceTypes pBasicReferenceType)
		{
			AdoUser.DeleteUserBasicReferenceValues (pUser, pBasicReferenceType);
		}
		

		public static void SetUserRightValue (User pUser, BasicReference pValue)
		{
			pValue.BasicReferenceType = new BasicReferenceType () { ID = (int) BasicReferenceTypes.UserRights };

			SetUserBasicReference (pUser, pValue);
		}

		
		public static void SetUserBasicReference (User pUser, BasicReference pValue)
		{
			AdoUser.SetUserBasicReferenceValue (pUser, pValue);
		}
		
		#endregion
	
	
		#region SignIn

		public static List<string> ListUserAlias(string pAliasPart, int pCount)
		{
			return AdoUser.ListUserAlias (pAliasPart, pCount);
		}


		public static User SignInByCredentials (User pUser)
		{
			User userDB;
			
		
			#region Validacion
			
			Validate.FilledObject (true, "Usuario", pUser);
			Validate.FilledObject (true, "Alias", pUser.Alias);
			Validate.FilledObject (true, "Password", pUser.Password);
			Validate.FilledObject (true, "Terminal", pUser.UserTerminal);
			Validate.Filled (true, "IP de la maquina", pUser.UserTerminal.UserHostAddress);
			
			pUser.SessionID = Guid.NewGuid ().ToString ();
			pUser.Password = TextHelper.EncryptRSA (pUser.Password);
			
			#endregion
		
			
			userDB = AdoUser.GetUserByCredentials (pUser);
		
			RegisterSignIn (userDB, pUser.UserTerminal);
			
			return userDB;
		}
		
		
		public static User SignInByIdentificationCredentials (User pUser)
		{
			User userDB;
		
			
			#region Validacion
			
			Validate.FilledObject (true, "Usuario", pUser);
			Validate.FilledObject (true, "Tipo de Identificacion", pUser.IdentificationType);
			Validate.FilledID ("Tipo de Identificacion", pUser.IdentificationType.ID);
			Validate.FilledObject (true, "Password", pUser.Password);
			Validate.FilledObject (true, "Terminal", pUser.UserTerminal);
			Validate.Filled (true, "IP de la maquina", pUser.UserTerminal.UserHostAddress);
			
			pUser.SessionID = Guid.NewGuid ().ToString ();
			pUser.Password = TextHelper.EncryptRSA (pUser.Password);
			
			#endregion
			
			userDB = AdoUser.GetUserByIdentificationCredentials (pUser);
				
			RegisterSignIn (userDB, pUser.UserTerminal);
			
			return userDB;
		}
		
		
		private static void RegisterSignIn (User pUserDB, Terminal pTerminal)
		{
			Terminal tmpTerminal;
		
			UserSessionLog userSessionLog;
			
			
			if (pUserDB == null)
			{
				throw new GenericException (SSOExceptionCodes.USER_INVALIDCREDENTIALS, "Credenciales Invalidas");
			}
			
			if (!pUserDB.IsEnabled.Value)
			{
				throw new GenericException (SSOExceptionCodes.USER_DISABLED, string.Format ("El usuario {0} esta deshabilitado", pUserDB.Alias));
			}

			pUserDB.UserTerminal = pTerminal;
			
			if (pUserDB.UserTerminal != null && !string.IsNullOrEmpty (pUserDB.UserTerminal.Code))
			{
				tmpTerminal = AdoTerminal.Get (pUserDB.UserTerminal);
				
				if (tmpTerminal == null)
				{
					throw new GenericException (SSOExceptionCodes.TERMINAL_INVALID, string.Format ("La Terminal {0} no es valida", pUserDB.UserTerminal.Code));
				}
				
				if (!tmpTerminal.IsEnabled.Value)
				{
					throw new GenericException (SSOExceptionCodes.TERMINAL_DISABLED, string.Format ("La Terminal {0} esta deshabilitada", pUserDB.UserTerminal.Code));
				}
				
				tmpTerminal.UserHostAddress = pUserDB.UserTerminal.UserHostAddress;
				
				pUserDB.UserTerminal = tmpTerminal;
			}
			
			userSessionLog = new UserSessionLog () { 
				ID = pUserDB.SessionID,
				User = pUserDB,
				StartDateTime = DateTime.Now };
		
			AdoUserSessionLog.Insert (userSessionLog);
		}
		
		#endregion
		
		
		#region Close Session
		
		public static void CloseSession (UserSessionLog pUserSessionLog)
		{
			Validate.FilledObject (true, "Sesion de Usuario", pUserSessionLog);
			Validate.FilledGuid (true, "Identificador de Sesión", pUserSessionLog.ID);
			
			pUserSessionLog.EndDateTime = DateTime.Now;
			
			AdoUserSessionLog.UpdateEnd (pUserSessionLog);
		}
		
		
		public static void CloseSessionByCredentials (User pUser)
		{
			#region Validacion
			
			Validate.FilledObject (true, "Usuario", pUser);
			Validate.FilledObject (true, "Alias", pUser.Alias);
			Validate.FilledObject (true, "Password", pUser.Password);
			Validate.FilledGuid (true, "Identificador de Sesión", pUser.SessionID);
			Validate.FilledObject (true, "Terminal", pUser.UserTerminal);
			Validate.Filled (true, "IP de la maquina", pUser.UserTerminal.UserHostAddress);
			
			pUser.Password = TextHelper.EncryptRSA (pUser.Password);
			
			#endregion
		
			using (ContextSingleTransactionScope TS = new ContextSingleTransactionScope (AdoESFSSO.W.ConnectionName))
			{
			if (AdoUser.IsThereUserByCredentials (pUser))
			{
				CloseSession (new UserSessionLog () { ID = pUser.SessionID });
			}
			else
			{
				throw new GenericException (SSOExceptionCodes.USER_INVALIDCREDENTIALS, string.Format ("Las credenciales del usuario {0} son invalidas", pUser.Alias));
			}
			TS.Complete ();
			}
		}
		
		
		public static void CloseSessionByIdentificationCredentials (User pUser)
		{
			#region Validacion
			
			Validate.FilledObject (true, "Usuario", pUser);
			Validate.FilledObject (true, "Tipo de Identificacion", pUser.IdentificationType);
			Validate.FilledID ("Tipo de Identificacion", pUser.IdentificationType.ID);
			Validate.FilledObject (true, "Password", pUser.Password);
			Validate.FilledGuid (true, "Identificador de Sesión", pUser.SessionID);
			Validate.FilledObject (true, "Terminal", pUser.UserTerminal);
			Validate.Filled (true, "IP de la maquina", pUser.UserTerminal.UserHostAddress);
			
			pUser.Password = TextHelper.EncryptRSA (pUser.Password);
			
			#endregion
			
			using (ContextSingleTransactionScope TS = new ContextSingleTransactionScope (AdoESFSSO.W.ConnectionName))
			{
			if (AdoUser.IsThereUserByIdentificationCredentials (pUser))
			{
				CloseSession (new UserSessionLog () { ID = pUser.SessionID });
			}
			else
			{
				throw new GenericException (SSOExceptionCodes.USER_INVALIDCREDENTIALS, string.Format ("Las credenciales de identificacion del usuario {0} son invalidas", pUser.Alias));
			}
			TS.Complete ();
			}
		}
		
		#endregion
		
		
		#region Menus
		
		public static UserMenuItem GetUserMenu (UserMenuItem pUserMenuItem)
		{
			pUserMenuItem.BasicReferenceType = new BasicReferenceType () { ID = (int) BasicReferenceTypes.UserMenu };

			return AdoUser.GetUserMenu (pUserMenuItem);
		}


		public static List<UserMenuItem> GetMainUserMenus(BasicReference pApplication, BasicReference pUserGroup)
		{
			return GetUserMenus (pApplication, pUserGroup, null);
		}


		public static List<UserMenuItem> GetUserMenus(BasicReference pApplication, BasicReference pUserGroup, BasicReference pParentUserMenu)
		{
			return AdoUser.GetUserMenus (pApplication, pUserGroup, pParentUserMenu);
		}


		public static List<UserMenuItem> GetMainUserMenus(BasicReference pApplication)
		{
			return GetUserMenus (pApplication, null);
		}


		public static List<UserMenuItem> GetUserMenus(BasicReference pApplication, BasicReference pParentUserMenu)
		{
			return AdoUser.GetUserMenus (pApplication, null, pParentUserMenu);
		}


		/*public static List<FileReference> GetUserMenuImages(User pUser, BasicReference pApplication)
		{
			List<FileReference> files;
		
			string sessionID;

		
			#region Validacion
			
			Validate.FilledObject (true, "User", pUser);
			Validate.Filled (true, "SessionID", pUser.SessionID);
			Validate.FilledObject (true, "Aplicación", pApplication);
			Validate.Filled (true, "Codigo de la Aplicación", pApplication.Code);
			
			#endregion
			
			sessionID = pUser.SessionID;

			pUser = CtrlUser.GetUserFromSessionID (pUser.SessionID);

			if (pUser == null) { throw new GenericException (string.Format ("El identificador de sesion {0} no corresponde a una sesion iniciada", sessionID)); }

			files = AdoUser.GetUserMenuImages (pUser, pApplication);
			
			// Se obtiene el contenido de cada archivo 
			foreach (FileReference f in files)
			{
				// TODO: Contemplar otras fuentes, por ahora solo locales
				f.String64Contents = Convert.ToBase64String (FileHelper.ReadFile (f.URL));
			}			
			
			return files;
		}*/


		public static List<UserMenuItem> GetUserApplicationList(User pUser)
		{
			return AdoUser.GetUserApplicationList (pUser);
		}


		public static List<UserMenuItem> GetUserMenu(User pUser, BasicReference pApplication)
		{
			#region Validacion
			
			Validate.FilledObject (true, "User", pUser);
			Validate.FilledObject (true, "Aplicación", pApplication);
			Validate.Filled (true, "Codigo de la Aplicación", pApplication.Code);
			
			#endregion
			
			return AdoUser.GetUserMenu (pUser, pApplication);
		}
		

		public static int? RegisterUserMenu (UserMenuItem pUserMenuItem)
		{
			pUserMenuItem.BasicReferenceType = new BasicReferenceType () { ID = (int) BasicReferenceTypes.UserMenu };

			return AdoUser.RegisterUserMenu (pUserMenuItem);
		}


		public static void DeleteUserMenu (UserMenuItem pUserMenuItem)
		{
			AdoUser.DeleteUserMenu (pUserMenuItem);
		}

		#endregion
		
		
		#region Recover Password
		
		public static void AutoResetPassword (User pUser)
		{
			#region Validacion
			
			Validate.FilledAuditableEntity ("Usuario", pUser);
			Validate.FilledID ("Identificador", pUser.ID);
			Validate.Filled (true, "Nombre (Login)", pUser.Alias);
			
			#endregion
			
			
			string strMsg;
			
			
			pUser.Password = PasswordHelper.Generate ();
			
			strMsg = string.Format ("Cordial Saludo.\n\nLa contraseña para su usuario {0} ha cambiado a:\n\n{1}\n\n" + 
					"Recuerde que el manejo de su cuenta de usuario es personal e intransferible.\n\n" + 
					"Para acceder a sus servicios visite:\n\n" + ReadConfig.MainURL,
					pUser.Alias, pUser.Password);
			
			pUser.Password = TextHelper.EncryptRSA (pUser.Password);
			
			AdoUser.SetPassword (pUser);
			
			pUser = Get (pUser);
			
			EmailSender.Send ("Reinicio de su Contraseña", strMsg, false, false, false, pUser.Email);
		}


		public static void SetNewPassword (string pAlias, string pNewPassword)
		{
			User user;


			#region Validacion
			
			Validate.Filled (true, "Nombre (Login)", pAlias);
			Validate.Filled (true, "Contraseña", pNewPassword);
			
			#endregion
			
			
			string strMsg;
			
			
			strMsg = string.Format ("Cordial Saludo.\n\nLa contraseña para su usuario {0} ha cambiado a:\n\n{1}\n\n" + 
					"Recuerde que el manejo de su cuenta de usuario es personal e intransferible.\n\n" + 
					"Para acceder a sus servicios visite:\n\n" + ReadConfig.MainURL,
					pAlias, pNewPassword);
			
			user = new User () { Alias = pAlias, Password = TextHelper.EncryptRSA (pNewPassword) };

			AdoUser.SetPassword (user);
			
			user = Get (user);
			
			EmailSender.Send ("Cambio de su Contraseña", strMsg, false, false, false, user.Email);
		}
		
		
		public static void RecoverPassword (User pUser)
		{
			#region Validacion
			
			Validate.Filled (true, "Correo Electrónico", pUser.Email);
			Validate.FilledObject (true, "Pregunta de Seguridad", pUser.SecurityQuestion);
			Validate.FilledID ("ID de la Pregunta de Seguridad", pUser.SecurityQuestion.ID);
			Validate.Filled (true, "Respuesta de la Pregunta de Seguridad", pUser.SecurityQuestionAnswer);
			
			#endregion
			
			pUser = AdoUser.RememberPassword (pUser);
			
			if (pUser == null || string.IsNullOrEmpty (pUser.Password))
			{
				throw new GenericInfoException ("Verifique su email y la respuesta a la pregunta secreta pues no coinciden con ningún usuario registrado");
			}
			else
			{
				pUser.Password = TextHelper.DecryptRSA (pUser.Password);
			
				EmailSender.Send ("Recordar su Contraseña", 
					string.Format ("Cordial Saludo.\n\nUsted ha solicitado recordar su contraseña para el usuario {2} asociado al email {0} " + 
						"previa validacion de su pregunta secreta y su email.\n\n" + 
						"Su contraseña es {1}.\n\n" + 
						"Recuerde que el manejo de su cuenta de usuario es personal e intransferible.\n\n" + 
						"Para acceder a sus servicios visite: {3}",
						pUser.Email, pUser.Password, pUser.Alias, ReadConfig.MainURL), false, false, false, pUser.Email);
			}
		}
		
		#endregion
		
		
		#region SignUp

		public static void Delete(List<User> pUsers)
		{
			using (ContextSingleTransactionScope TS = new ContextSingleTransactionScope (AdoESFSSO.W.ConnectionName))
			{
				foreach (User u in pUsers)
				{
					AdoUser.Delete (u);
				}
				
				TS.Complete ();
			}
		}
		
		
		public static int SignUp (User pUser)
		{
			bool isNewAccount;
			
			string originalPass = string.Empty;


			isNewAccount = (pUser.ID == null);

			#region Validacion
			
			Validate.Filled (true, "Alias", pUser.Alias);
			
			Validate.FilledBirthDate (false, "Cumpleaños", pUser.BirthDate, 18);
			Validate.FilledEmail (true, "Email", pUser.Email);
			Validate.Filled (true, "Número de Identificación", pUser.IdentificationNumber);
			Validate.FilledID ("Tipo de Identificación", pUser.IdentificationType);
			Validate.Filled (true, "Primer Nombre", pUser.FirstName);
			Validate.Filled (true, "Primer Apellido", pUser.SurName);
			Validate.FilledID ("Pregunta de Seguridad", pUser.SecurityQuestion);
			Validate.Filled (true, "Respuesta de la Pregunta de Seguridad", pUser.SecurityQuestionAnswer);
			
			Validate.FilledList (true, "Ubicación", pUser.Locations);
			
			pUser.UserCreationAlias = User.ROOT_ALIAS;
			
			if (isNewAccount)
			{
				if (string.IsNullOrEmpty (pUser.Password))
				{
					pUser.Password = PasswordHelper.Generate ();
				}

				originalPass = pUser.Password;

				pUser.Password = TextHelper.EncryptRSA (pUser.Password);
			}
			
			Validate.FilledAuditableEntity ("Usuario", pUser);
			
			
			#region Person Location
			
			foreach (Location pl in pUser.Locations)
			{
				Validate.Filled (true, "Dirección", pl.Address);
				//Validate.Filled (true, "Celular", pl.CellPhoneNumber);
				Validate.Filled (true, "Fijo", pl.PhoneNumber);
				Validate.FilledBasRefPart ("Ciudad", pl.City);
				Validate.FilledBasRefPart ("País", pl.Country);
				Validate.Filled (true, "Nombre", pl.Name);
			}
			
			#endregion
			
			#endregion
		
			using (ContextSingleTransactionScope TS = new ContextSingleTransactionScope (AdoESFSSO.W.ConnectionName))
			{
				AdoUser.Register (ref pUser);
				
				TS.Complete ();
			}
			
			
			if (pUser.NotifyUserEmail)
			{
				if (pUser.IsEnabled.Value)
				{
					EmailSender.Send (
						"Cuenta Actualizada", 
						"Cordial Saludo.\n\nSu cuenta de usuario " + pUser.Alias + " ha sido actualizada." + 
						(isNewAccount ? "\n\nSu contraseña es: " + originalPass : string.Empty) +
						"\n\nEsta disponible para ser utilizada.\n\nSus datos basicos a continuación:\n\n" + 
						pUser.ToString () + 
						"\n\nPara acceder a sus servicios visite:\n\n" + ReadConfig.MainURL,
						false, false, false, pUser.Email);
				}				
				else 
				{
					EmailSender.Send (
						"Cuenta Deshabilitada", 
						"Cordial Saludo.\n\nSu cuenta de usuario " + pUser.Alias + " ha sido temporalmente deshabilitada\n\n" + 
						"Si tiene alguna duda por favor notifiquela mediante la sección Contáctenos del sitio en:\n\n" + ReadConfig.MainURL,
						false, false, false, pUser.Email);	
				}
			}

			return pUser.ID.Value;
		}
		
		#endregion
		
		
		#region Consulta Sesion
		
		public static User GetUserFromSessionID (string pSessionID)
		{
			return AdoUser.GetUserFromSessionID (pSessionID);
		}
		
		#endregion
		

		#region Messages

		public static void SendMessage (string pToEmail, User pSenderUser, string pSubject, string pBody)
		{
			MailMessage msg;


			msg = new MailMessage ();

			msg.To.Add (pToEmail);
			msg.ReplyTo = new MailAddress (pSenderUser.Email, pSenderUser.calFullName);
			msg.Subject = pSubject;
			msg.IsBodyHtml = false;
			msg.Body = pBody;

			EmailSender.Send (msg);
		}


		public static void SendMessage (string [] pToEmails, User pSenderUser, string pMsgSubject, string pMsgBody)
		{
			if (DataHelper.IsEmpty (pToEmails)) { return; }


			ProcessHelper.DoAndNotifyEmail 
			(
				"SendMessage",

				pMsgSubject,

				delegate (ref string pSubject, ref List<string> pTo, ref StringBuilder pBody, ref TextWriter pLogWriter, DateTime pStartDateTime)
				{
					int indexOK;

					MailMessage msg;


					msg = new MailMessage ();
									
					msg.ReplyTo = new MailAddress (pSenderUser.Email, pSenderUser.calFullName);
					msg.Subject = pMsgSubject;
					msg.IsBodyHtml = false;
					msg.Body = pMsgBody;

					indexOK = 0;
					

					foreach (string toEmail in pToEmails)
					{
						msg.To.Clear ();
						msg.To.Add (toEmail);

						try
						{
							EmailSender.Send (msg, true, true);
							indexOK ++;
						}
						catch (Exception pEx)
						{
							Log.Error (pEx);

							pBody.AppendFormat ("No fue enviado a {0}: {1}", toEmail, pEx.Message);
							pBody.AppendLine ();
						}
					}

					if (indexOK == pToEmails.Length)
					{
						pBody.Insert (0, string.Format ("Fue enviado correctamente a TODOS los {0} destinatarios", indexOK));
					}
					else
					{
						pBody.Insert(0, string.Format("Fue enviado correctamente a {0} de {1} destinatarios\n\n", indexOK, pToEmails.Length));
					}


					pTo.Add (pSenderUser.Email);
					pSubject = string.Format("Se ha enviado \"{0}\" a {1} de {2} destinatarios", pMsgSubject, indexOK, pToEmails.Length);
					
				});
		}


		public static void SendMessageForAll (User pSenderUser, bool? pUserToIsEnabled, string pSubject, string pBody)
		{
			SendMessage (GetAllEmails (pUserToIsEnabled).ToArray (), pSenderUser, pSubject, pBody);
		}


		public static List<string> GetAllEmails(bool? pUserIsEnabled)
		{
			return AdoUser.GetAllEmails (pUserIsEnabled);
		}

		#endregion

		
		#region Browse
		
		public static BrowseResults<User> Browse (User pSample, PaggingInfo pPagInfo)
		{
			return AdoUser.Browse (pSample, pPagInfo);
		}
		
		#endregion
		
		
		#region Edit
		
		public static User Get (User pUser)
		{
			#region Validacion
			
			Validate.FilledID ("ID", pUser.ID);
			
			#endregion
			
			return AdoUser.Get (pUser);
		}
		
		#endregion
	}
}
